﻿using System;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Threading;
using Game.Common;
using Game.Level;
using Game.Map;

namespace Game.Actions
{
    public delegate void ExecuteHandler(GameAction action);
    
    public class ActionExecutorEventArgs : EventArgs 
    {
        readonly GameTurn _gameTurn;
        readonly bool _canChain;

        public ActionExecutorEventArgs(GameTurn turn, bool canChain)
        {
            _gameTurn = turn;
            _canChain = canChain;
        }

        public bool CanChain
        {
            get { return _canChain; }
        }

        public GameTurn GameTurn
        {
            get { return _gameTurn; }
        }

    }
    
    public class ActionExecutor : IActionCallback
    {
        public event EventHandler<ActionExecutorEventArgs> After;
        public event EventHandler IsRunningChanged;

        readonly UserControl _userControl= new UserControl();
        readonly List<GameAction> _actionsToRunLater = new List<GameAction>();

        int _actionsRunning;
        GameAction _executingAction;
        
        public void Execute(
            IGameMapView gameMapView,
            GameLevels levels,
            GameTurn turn,
            GameAction action)
        {
            _actionsRunning++;
            FireIsRunningChanged();
            _executingAction = action;

            if (_actionsToRunLater.Contains(action))
            {
                _actionsToRunLater.Remove(action);
            }

            var context = new Context(this, gameMapView, levels);
            action.DoExecute(turn, context);

            _actionsRunning--;
            FireIsRunningChanged();
            _executingAction = null;

            GameDebug.Assert(_actionsRunning >= 0);
        }

        public GameAction ExecutingAction
        {
            get { return _executingAction; }
        }

        public bool IsRunning
        {
            get { return (_actionsRunning > 0 || _actionsToRunLater.Count > 0); }
        }

        public void Start(GameTurn turn)
        {
            _actionsRunning++;
            GameDebug.Assert(_actionsRunning >= 0);
            FireIsRunningChanged();
        }

        public void Done(GameTurn turn, bool canChain)
        {
            _actionsRunning--;
            GameDebug.Assert(_actionsRunning >= 0);

            FireIsRunningChanged();
            FireAfter(turn, canChain);
        }
        
        public void ExecuteLater(
            IGameMapView view,
            GameLevels levels,
            GameTurn turn,
            GameAction action)
        {
            var d = _userControl.Dispatcher;
            
            var child = turn.CreateChild();
            child.PlayerWithTurn = AiPlayer.Player;
            var work = new Work(action, this, child, view, levels);
            
            Delegate method = (Action)work.DoAction;
            d.BeginInvoke(DispatcherPriority.Normal, method);

            _actionsToRunLater.Add(action);
        }
        
        internal void FireAfter(GameTurn turn, bool canChain)
        {
            if (After != null)
            {
                After(this, new ActionExecutorEventArgs(turn, canChain));
            }
        }

        void FireIsRunningChanged()
        {
            if (IsRunningChanged != null)
            {
                IsRunningChanged(this, new EventArgs());
            }
        }
    }
    
    public class Work 
    {
        readonly GameAction _gameAction;
        readonly ActionExecutor _actionExecutor;
        readonly GameTurn _gameTurn;
        readonly IGameMapView _gameMapView;
        readonly GameLevels _gameLevels;
        
        public Work(
            GameAction gameAction,
            ActionExecutor actionExecutor,
            GameTurn turn,
            IGameMapView gameMapView,
            GameLevels gameLevels)
        {
            _gameAction = gameAction;
            _actionExecutor = actionExecutor;
            _gameTurn = turn;
            _gameMapView = gameMapView;
            _gameLevels = gameLevels;
        }
        
        public void DoAction()
        {
            _actionExecutor.Execute(
                _gameMapView, 
                _gameLevels,
                _gameTurn, 
                _gameAction);
        }
    }
    
}
